#include <basic/errcode.h>
#include <basic/shm.h>

#ifdef __Solaris__
#else
#if defined(__linux__)
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};
#endif
static int _oflag=(S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH);
static union semun _op_arg[2]={{0}, {1}};
static struct sembuf _op_lock={0,-1,0};
static struct sembuf _op_unlock={0,1,0};
//static struct sembuf _op_trywait={0,-1,IPC_NOWAIT};
#endif

#if defined(__linux__)
#define _SHM_FILE(_file, _area_file)    \
sprintf(_file, "/%s", _area_file)

#elif defined(__Solaris__)
#define _SHM_FILE(_file, _area_file)    \
sprintf(_file, "/%s", _area_file)

#elif defined(__FreeBSD__) || defined(__APPLE__)
#define _SHM_FILE(_file, _area_file)    \
sprintf(_file, "/tmp/%s", _area_file)

#endif

#define AREA_NEED_LOCK  (head->flag & SHM_AREA_LOCK)

/*==========*/
/* 共享区域 */
/*==========*/
struct _shm_area_head
{/*{{{*/
    unsigned int flag;
    int area_size;
#ifdef __Solaris__
    sem_t lock;
#else
    int lock;
#endif
};/*}}}*/

int shm_area_create(char *area_file, int area_size, unsigned int flag)
{/*{{{*/
    char file[LONG_FILE_NM_SIZE];
    int fd;
    struct _shm_area_head *head;
    int ret;
#ifdef __Solaris__
#else
    //struct stat st;
    unsigned int hash;
    int sem_id; 
#endif

    if(!area_file ||
            area_size <= 0 || area_size > SHM_AREA_MAX_SIZE)
        return(-1);

    _SHM_FILE(file, area_file);

    // 删除映射文件
#ifdef __APPLE__
#else
    if(shm_unlink(file))
    {
        if(get_last_error(NULL) != ENOENT)
            return(-2);
    }
#endif

    // 创建映射文件
    area_size+=pack_size(sizeof(struct _shm_area_head), 4);
    area_size=pack_size(area_size, getpagesize());
    if((fd=shm_open(file, O_RDWR | O_CREAT, _oflag)) == -1)
        return(-3);
    ftruncate(fd, area_size);
    head=(struct _shm_area_head *)mmap(NULL, area_size,
            PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);
    if(head == MAP_FAILED)
    {   return(-4);   }

    // 初始化
    head->flag=flag;
    head->area_size=area_size;
    if(AREA_NEED_LOCK)
    {
#ifdef __Solaris__
        sem_init(&head->lock, 1, 1);
#else
        hash=get_hash((unsigned char *)file, strlen(file));
        if((sem_id=semget((key_t)hash, 1,
                        IPC_CREAT | IPC_EXCL | _oflag)) == -1)
        {
            if(get_last_error(NULL) != EEXIST)
            {   ret=-6; goto OUT;   }
            if((sem_id=semget((key_t)hash, 1, _oflag)) == -1)
            {   ret=-7; goto OUT;   }
        }
        if(semctl(sem_id, 0, SETVAL, _op_arg[1]) == -1)
        {   ret=-8; goto OUT;   }
        head->lock=sem_id;
#endif
    }
    ret=0;

OUT:
    munmap((void *)head, area_size);
    return(ret);
}/*}}}*/

int shm_area_destroy(char *area_file)
{/*{{{*/
    char file[LONG_FILE_NM_SIZE];

    if(area_file == NULL)
        return(-1);

    _SHM_FILE(file, area_file);

    // 删除映射文件
#ifdef __APPLE__
#else
    if(shm_unlink(file))
    {
        if(get_last_error(NULL) != ENOENT)
        {
            return(-2);
        }
    }
#endif

    return(0);
}/*}}}*/

void *shm_area_attach(char *area_file)
{/*{{{*/
    char file[LONG_FILE_NM_SIZE];
    int fd;
    struct _shm_area_head *head;
    int area_size;

    if(!area_file)
        return(NULL);

    _SHM_FILE(file, area_file);
    if((fd=shm_open(file, O_RDWR, 0)) == -1)
        return(NULL);
    head=(struct _shm_area_head *)mmap(NULL, getpagesize(),
            PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(head == MAP_FAILED)
    {   close(fd); return(NULL);   }
    area_size=head->area_size;
    munmap((void *)head, getpagesize());
    head=(struct _shm_area_head *)mmap(NULL, area_size,
            PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);
    if(head == MAP_FAILED)
        return(NULL);

    return((void *)head + pack_size(sizeof(struct _shm_area_head), 4));
}/*}}}*/

int shm_area_detach(void *area) 
{/*{{{*/
    struct _shm_area_head *head;

    if(!area)
        return(-1);

    head=(struct _shm_area_head *)
        (area - pack_size(sizeof(struct _shm_area_head), 4));
    munmap((void *)head, head->area_size);
    return(0);
}/*}}}*/

int shm_area_enter(void *area)
{/*{{{*/
    struct _shm_area_head *head;

    if(!area)
        return(-1);

    head=(struct _shm_area_head *)
        (area - pack_size(sizeof(struct _shm_area_head), 4));
    if(AREA_NEED_LOCK)
    {
#ifdef __Solaris__
        sem_wait(&head->lock);
#else
        semop(head->lock, &_op_lock, 1);
#endif
    }
    return(0);
}/*}}}*/

int shm_area_leave(void *area)
{/*{{{*/
    struct _shm_area_head *head;

    if(!area)
        return(-1);

    head=(struct _shm_area_head *)
        (area - pack_size(sizeof(struct _shm_area_head), 4));
    if(AREA_NEED_LOCK)
    {
#ifdef __Solaris__
        sem_post(&head->lock);
#else
        semop(head->lock, &_op_unlock, 1);
#endif
    }
    return(0);
}/*}}}*/

/*================*/
/* 共享内存通信槽 */
/*================*/
//#define DEFAULT_HANDLE_NUM    10
//#define SLOT    shm_slots[shm_handle]
//
//#ifdef __Solaris__
//#define WAIT_FREE	sem_wait(&SLOT.shm->a_sem[0])
//#define POST_FREE	sem_post(&SLOT.shm->a_sem[0])
//#define WAIT_USED	sem_wait(&SLOT.shm->a_sem[1])
//#define POST_USED	sem_post(&SLOT.shm->a_sem[1])
//#define WAIT_SYNC	sem_wait(&SLOT.shm->a_sem[2])
//#define POST_SYNC	sem_post(&SLOT.shm->a_sem[2])
//#else
//#define WAIT_FREE	semop(SLOT.shm->a_sem[0], &_op_lock, 1)	
//#define POST_FREE	semop(SLOT.shm->a_sem[0], &_op_unlock, 1)	
//#define WAIT_USED	semop(SLOT.shm->a_sem[1], &_op_lock, 1)	
//#define POST_USED	semop(SLOT.shm->a_sem[1], &_op_unlock, 1)	
//#define WAIT_SYNC	semop(SLOT.shm->a_sem[2], &_op_lock, 1)	
//#define POST_SYNC	semop(SLOT.shm->a_sem[2], &_op_unlock, 1)	
//#endif
//
//struct _shm_queue
//{
//#ifdef __Solaris__
//    sem_t a_sem[3];
//#else
//    int a_sem[3];
//#endif
//    unsigned char msg[SHM_MSG_SIZE];
//    int msg_size;
//};
//
///*
//struct _shm_slot
//{
//    struct _shm *shm;
//};
//*/
//
///*
//static struct _shm_slot *shm_slots;
//static int shm_slots_num;
//static pthread_mutex_t shm_slots_lock;
//
//
//static void _init_slot(SHM_HANDLE shm_handle)
//{
//    SLOT.shm=NULL;
//}
//
//
//int shm_startup(int handle_num)
//{
//    SHM_HANDLE shm_handle;
//
//    if(handle_num <= 0)
//        handle_num=DEFAULT_HANDLE_NUM;
//    ALLOC_ARRAY_RETERR(shm_slots, struct _shm_slot, handle_num,
//        shm_slots_num, -1);
//    for(shm_handle=0; shm_handle<handle_num; shm_handle++)
//        _init_slot(shm_handle);
//    pthread_mutex_init(&shm_slots_lock, NULL);
//
//    return(0);
//}
//
//
//int shm_cleanup()
//{
//    SHM_HANDLE shm_handle;
//
//    for(shm_handle=0; shm_handle<shm_slots_num;
//        shm_handle++)
//        if(SLOT.shm != NULL)
//            shm_release(shm_handle);
//    FREE_ARRAY(shm_slots, shm_slots_num);
//
//    return(0);
//}
//*/
//
//
//SHMQ_HANDLE shmq_clt_apply(char *shm_file)
//{
//    SHMQ_HANDLE handle;
//    int fd;
//    char virt_file_nm[LONG_FILE_NM_SIZE];
//
//    if(!shm_file)
//        return(NULL);
//
//    // 映射共享内存
//    VIRT_SHM_FILE(virt_file_nm, shm_file);
//    if((fd=shm_open(file_nm, O_RDWR, 0)) == -1)
//        return(NULL);
//    handle=(struct _shm_queue *)mmap(NULL, sizeof(struct _shm_queue),
//                    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
//    close(fd);
//    if(handle == MAP_FAILED)
//        return(NULL);
//
//    return(handle);
//}
//
//
//SHMQ_HANDLE shmq_svr_apply(char *shm_file)
//{
//    SHMQ_HANDLE handle;
//    int fd;
//    char virt_file_nm[LONG_FILE_NM_SIZE];
//#ifdef __SOLARIS__
//#else
//    struct stat stat;
//    int sem_id; 
//    int i;
//#endif
//
//    if(!shm_file)
//        return(NULL);
//
//    // 映射共享内存
//    VIRT_SHM_FILE(virt_file_nm, shm_file);
//#ifdef __APPLE__
//#else
//    if(shm_unlink(virt_file_nm))
//    {
//        if(get_last_error(NULL) != ENOENT)
//        {
//            //printf("file: %s error: %d\n", file_nm, get_last_error(NULL));
//            return(NULL);
//        }
//    }
//#endif
//    if((fd=shm_open(file_nm, O_RDWR|O_CREAT, _oflag)) == -1)
//        return(NULL);
//    ftruncate(fd, sizeof(struct _shm_queue)+128);
//    if((handle=(struct _shm_queue *)mmap(NULL,
//                    sizeof(struct _shm_queue), PROT_READ | PROT_WRITE,
//                    MAP_SHARED, fd, 0)) == MAP_FAILED)
//    {   close(fd); return(NULL);   }
//
//#ifdef __Solaris__
//	sem_init(&SLOT.shm->a_sem[0], 1, 1);
//	sem_init(&SLOT.shm->a_sem[1], 1, 0);
//	sem_init(&SLOT.shm->a_sem[2], 1, 0);
//#else
//    if(fstat(fd, &stat) == -1)
//        goto OUT_UNMAP;
//	for(i=0; i<3; i++)
//	{
//        if((sem_id=semget((key_t)stat.st_ino*3+i, 1,
//                        IPC_CREAT | IPC_EXCL | _oflag)) == -1)
//		{
//			if(get_last_error(NULL) != EEXIST ||
//                    (sem_id=semget(shm_key*16+i, 1, _oflag)) == -1)
//                goto OUT_UNMAP;
//		}
//		handle->sems[i]=sem_id;
//	}
//    if(semctl(handle->sems[0], 0, SETVAL, _op_arg[1]) == -1 ||
//            semctl(handle->sems[1], 0, SETVAL, _op_arg[0]) == -1 ||
//            semctl(handle->sems[2], 0, SETVAL, _op_arg[0]) == -1)
//        goto OUT_UNMAP;
//#endif
//
//    close(fd);
//    return(handle);
//
//OUT_UNMAP:
//    close(fd);
//    munmap((void *)handle, sizeof(struct _shm_queue));
//    return(NULL);
//}
//
//
//int shmq_release(SHMQ_HANDLE handle)
//{
//    if(!handle)
//        return(-1);
//
//    munmap((void *)handle, sizeof(struct _shm_queue));
//    return(0);
//}
//
//
//int shmq_clt_call(SHMQ_HANDLE handle,
//        char msg_send*, int msg_send_size,
//        char *msg_recv, int *msg_recv_size)
//{
//    if(!handle < 0 ||
//    !msg_send == NULL || msg_send_size <= 0 || msg_send_size > SHMQ_SIZE ||
//        recv == NULL || recv_size == NULL)
//        return(-1);
//
//    // 等待槽位空闲
//    if(WAIT_FREE)  return(-3);
//
//    // 填入请求
//    memcpy(SLOT.shm->msg, send, send_size);
//    SLOT.shm->msg_size=send_size;
//
//    // 指明槽位已占用
//    POST_USED;
//
//    // 等待应答
//    if(WAIT_SYNC)  return(-4);
//
//    // 处理应答
//    if(SLOT.shm->msg_size <= 0 || SLOT.shm->msg_size > SHM_MSG_SIZE)
//    {
//        POST_FREE;
//        return(-5);
//    }
//    memcpy(recv, SLOT.shm->msg, SLOT.shm->msg_size);
//    *recv_size=SLOT.shm->msg_size;
//
//    // 指明槽已空
//    POST_FREE;
//
//    return(0);
//}
//
//
//int shm_svr_recv(SHM_HANDLE shm_handle, unsigned char *recv, int *recv_size)
//{
//    if(shm_handle < 0 || shm_handle >= shm_slots_num ||
//        SLOT.shm == NULL)
//        return(-1);
//    if(recv == NULL || recv_size == NULL)
//        return(-2);
//
//    // 等待槽位占用
//    if(WAIT_USED)  return(-3);
//
//    // 获得请求消息
//    memcpy(recv, SLOT.shm->msg, SLOT.shm->msg_size);
//    *recv_size=SLOT.shm->msg_size;
//
//    return(0);
//}
//
//
//int shm_svr_resp(SHM_HANDLE shm_handle, unsigned char *resp, int resp_size)
//{
//    if(shm_handle < 0 || shm_handle >= shm_slots_num ||
//        SLOT.shm == NULL)
//        return(-1);
//    if(resp == NULL || resp_size <= 0 || resp_size > SHM_MSG_SIZE)
//        return(-2);
//
//    // 填入应答消息
//    memcpy(SLOT.shm->msg, resp, resp_size);
//    SLOT.shm->msg_size=resp_size;
//
//    // 通知客户端
//    POST_SYNC;
//
//    return(0);
//}
